#!/usr/bin/env python
# encoding: utf-8


"""
@file: xingzhuang.py
@time: 2016/11/28 下午2:56
"""
# 三角形 形状的问题
from functools import reduce
from mathsolver.functions.base import *
from sympy import pi, trigsimp, symbols, fraction, cancel, simplify, solveset, expand_trig, sin, cos
from mathsolver.functions.sanjiao import sanjiao_utils as su
from operator import itemgetter
from mathsolver.functions.budengshi import common_opers as co
from mathsolver.functions.sanjiao.bianjiaozhuanhuan import SIDES, ANGLES, JiaoHuaBian, BianHuaJiao
from itertools import permutations
from mathsolver.functions.sanjiao.huajian import SanJiaoHuaJian
from mathsolver.functions.sanjiao import sanjiaoxing_utils

RIGHT_ANGLE_CONDS = map(lambda _: (_[0] ** 2 + _[1] ** 2 - _[2] ** 2), permutations(list(SIDES), 3))  # 直角的情况
EQ_SIDE_CONDS = map(lambda _: (_[0] - _[1], _[0] ** 2 - _[1] ** 2), permutations(SIDES, 2))  # 等边三角形情况


# 根据边角的关系, 角的关系判断三角形形状
def triangle_shape(eq):
    l_expr, r_expr = eq.sympify()
    f = l_expr - r_expr
    f, _ = fraction(cancel(f))
    symbs = FiniteSet(*f.free_symbols)
    shape = ''
    if symbs.is_subset(SIDES):  # 边的关系
        # 直角可能的形式
        for cond in RIGHT_ANGLE_CONDS:
            if abs(simplify(cond / f)) == 1:
                shape = '直角'
                break
        for cond in EQ_SIDE_CONDS:
            if abs(simplify(cond[0] / f)) == 1 or abs(simplify(cond[1] / f)) == 1:
                shape = '等腰'
                break
    elif symbs.is_subset(ANGLES):  # 角的关系
        pass
    else:
        raise Exception('try error')
    return shape


a, b, c = symbols('a, b, c')  # 边
COS_A = (b ** 2 + c ** 2 - a ** 2) / (2 * b * c)
COS_B = (a ** 2 + c ** 2 - b ** 2) / (2 * a * c)
COS_C = (a ** 2 + b ** 2 - c ** 2) / (2 * a * b)


def triangle_shape002(sides_values):
    """
    已知其中两条边的值，判断三角形形状
    :param sides_values: 边的值
    :return:
    """
    cos_a_v = simplify(COS_A.subs(sides_values))
    cos_b_v = simplify(COS_B.subs(sides_values))
    cos_c_v = simplify(COS_C.subs(sides_values))
    side_shape = ''
    # 边的形状
    if cos_a_v == cos_b_v == cos_c_v:
        side_shape = '等边'
        return side_shape
    elif cos_a_v == cos_b_v or cos_b_v == cos_c_v:
        side_shape = '等腰'
    angle_shape = ''
    if cos_a_v < 0 or cos_b_v < 0 or cos_c_v < 0:
        angle_shape = '钝角'
    elif cos_a_v == 0 or cos_b_v == 0 or cos_c_v == 0:
        angle_shape = '直角'
    elif cos_a_v > 0 and cos_b_v > 0 and cos_c_v > 0:
        angle_shape = '锐角'
    return side_shape + angle_shape


def triangle_shape003(trig_value):
    """
    根据三角函数的值，判断三角形形状.sinA, cosA。
    因为sin不管锐角，钝角永远都是正的，所以除非判断sinA=1。
    :param trig_value:
    :return:
    """
    raise Exception('Exception')


# 最基本的知道三角形的三条边 a, b, c
# 锐\直\钝\等边\等腰
# 001.三边判断等边/等腰三角形
# input a,b,c的值(BaseEqs)
# 在△ABC中,已知a=8,b=10,c=6判断△ABC的形状()
class XingZhuang001(BaseFunction):
    def solver(self, *args):
        self.label.add('判断三角形形状')
        eqs = args[0].sympify()
        tmp_f = reduce(lambda _1, _2: _1 + _2, map(lambda _: _[0] + _[1], eqs))
        symbs = tmp_f.free_symbols
        # 先检测是否满足该类题型
        if not FiniteSet(*symbs) == FiniteSet(*symbols('a, b, c')) or len(eqs) != 3:
            raise Exception('type not match')
        for ll, r in eqs:
            l_s = sympify(ll)
            r_v = sympify(r)
            if not (l_s.is_Symbol and r_v.is_real or l_s.is_real and r_v.is_Symbol):
                raise Exception('type not match')
        sorted_sides = sorted(eqs, key=itemgetter(1))
        side_type = ''  # 边的类型
        aa, bb, cc = sorted_sides  # a < b < c
        if aa[1] == bb[1] == cc[1]:
            side_type = '等边'
        elif bb[1] == aa[1] or bb[1] == cc[1]:
            side_type = '等腰'
        angle_type = ''  # 角的类型
        if aa[1] ** 2 + bb[1] ** 2 == cc[1] ** 2:
            angle_type = '直角'
        elif aa[1] ** 2 + bb[1] ** 2 > cc[1] ** 2:
            angle_type = '锐角'
        elif aa[1] ** 2 + bb[1] ** 2 < cc[1] ** 2:
            angle_type = '钝角'
        shape = side_type + angle_type
        self.steps.append(['三角形为' + shape, '三角形'])
        self.output.append(BaseString(shape))
        return self


# 三角形形状。根据三角来判断
# input: A,B,C 三个角的的角度值(BaseEqs)
# output: 三角形形状
class XingZhuang002(BaseFunction):
    def solver(self, *args):
        raise Exception('try error')
        return self


# input: 包含边关系的一个等式(BaseEq)
class XingZhuang003(BaseFunction):
    def solver(self, *args):
        self.label.add('判断三角形形状')
        eq = args[0]
        if not (len(args) == 1 and isinstance(args[0], BaseEq)):
            raise Exception('type match error')
        l_expr, r_expr = eq.sympify()
        f = l_expr - r_expr
        symbs = FiniteSet(*f.free_symbols)
        if not symbs.is_subset(FiniteSet(*symbols('A, B, C'))):  # 检测是否只包含角
            raise 'type match error'
        raise Exception('Type Match Error')
        return self


# input: 只包含角关系(BaseEq)
# 比如 在△ABC中,若cosBsinC=sinA,则△ABC的形状一定是()
class XingZhuang004(BaseFunction):
    def solver(self, *args):
        self.label.add('判断三角形形状')
        if not (len(args) == 1 and isinstance(args[0], BaseEq)):
            raise Exception('type match error')
        eq = args[0]
        l_expr, r_expr = eq.sympify()
        f = l_expr - r_expr
        symbs = FiniteSet(*f.free_symbols)
        self.steps.append(['三角形形状为:', ''])
        if not symbs.is_subset(FiniteSet(*symbols('A, B, C'))):  # 检测是否只包含角
            raise Exception('type match error')
        # 角转化为边的关系
        result_eqs = JiaoHuaBian(verbose=True).solver(eq).output[0].sympify()
        shapes = []
        for tmp_l, tmp_r in result_eqs:
            tmp_f = tmp_l - tmp_r
            tmp_shape = triangle_shape(BaseEq([tmp_f, '0']))
            if tmp_shape:
                shapes.append(tmp_shape)
        for shape in shapes:
            self.output.append(str(shape))
        return self


# input: paramer1:边角关系(BaseEq)
class XingZhuang005(BaseFunction):
    def solver(self, *args):
        self.label.add('判断三角形形状')
        if not (len(args) == 1 and isinstance(args[0], BaseEq)):
            raise Exception('type match error')
        eq = args[0]
        self.steps.append(['三角形形状为:', ''])
        l_expr, r_expr = eq.sympify()
        f = l_expr - r_expr
        # ---使用角化边关系, 如果使用角化边之后，关系式中含有r则表示不能用角化边，则专用边化角---------
        eqs = JiaoHuaBian(verbose=True).solver(BaseEq([f, '0'])).output[0].sympify()
        eqs_symbs = co.base_eqs_symbs(eqs)
        if FiniteSet(*eqs_symbs).is_subset(SIDES):
            # ----判断三角形形状---------
            shapes = []
            for tmp_eq in eqs:
                shape = triangle_shape(BaseEq(tmp_eq))
                shapes.append(shape)
            for tmp_shape in shapes:
                self.output.append(str(tmp_shape))
            return self
        angle_eq = BianHuaJiao(verbose=True).solver(BaseEq([f, '0'])).output[0].sympify()
        angle_eq_f = angle_eq[0] - angle_eq[1]
        angle_eq_f = trigsimp(angle_eq_f)
        two_angle_subs = (('A+B', 'pi - C'), ('A+C', 'pi -B'), ('B+C', 'pi -A'))
        angle_eq_f = angle_eq_f.subs(two_angle_subs)
        symbs = list(angle_eq_f.free_symbols)
        shape = ''
        if len(symbs) == 1:
            self.steps.append(['由题意可知', '' + BaseEq([angle_eq_f, ' 0']).printing()])
            angle_value = su.solve_trig_eq(BaseEq([angle_eq_f, '0']), Interval.open(0, pi))
            if angle_value[0] == pi / 2:
                shape += '直角'
            self.steps.append(['所以三角形为', '直角三角形'])
        else:
            raise Exception('Type Match Error')
        self.output.append(str(shape))
        return self


# input: paramer1:多个边角关系(BaseEqs)
# 我的思路:有两种方式 1 首先把所有关系化成边关系, 联立边的关系方程组。 2 把所有关系转换为角的关系，联立角的关系方程组
# 方程组有以下几种情况
# 1 三个方程组 可以求出 三个参数(a, b, c 或者 A, B, C)的值 这样直接用 根据 三边或者三角的值来判定三角形形状
# 2 两个方程组 然后利用把一个未知数作为常数，表示另外两个变量的值。 然后进行判断

class XingZhuang006(BaseFunction):
    @staticmethod
    def base_eqs_symbs(base_eqs):
        """
        等式中有哪些变量
        :param base_eqs:
        :return:
        """
        eqs_list = co.to_base_list(base_eqs)
        symbs = []
        for l_expr, r_expr in eqs_list:
            eq_f = l_expr - r_expr
            symbs.extend(list(eq_f.free_symbols))
        symbs = sorted(map(str, set(symbs)))
        return map(sympify, symbs)

    @staticmethod
    def legal_sided(side_v):
        """
        是否是合法的边
        :param side_v:
        :return:
        """
        base_intl = Interval(0, S.Infinity, left_open=True)
        s_intl = solveset(str(side_v) + '>0', domain=S.Reals)
        last_intl = base_intl.intersect(s_intl)
        return last_intl

    @staticmethod
    def legal_sides(side_values):
        """
        是否是合法的三边的值. 是否满足两边之和大于第三边。
        ((a, c/2), (b /c/4), (c, c))
        :param side_values:
        :return:
        """
        pers = permutations(side_values, 3)
        for per in pers:
            f_v, s_v, t_v = per
            if not solveset(str(f_v + s_v - t_v) + '>0', domain=S.Reals).intersect(
                    Interval(0, S.Infinity, left_open=True)):
                return False
        return True

    def solver(self, *args):
        self.label.add('判断三角形形状')
        base_eqs = map(BaseEq, args[0].sympify())
        sides_eqs = map(lambda eq: JiaoHuaBian(verbose=True).solver(eq).output[0].sympify(), base_eqs)  # 所有关系化为边关系
        # eqs_symbs = co.base_eqs_symbs(sides_eqs[0])
        # if FiniteSet(*eqs_symbs).is_subset(SIDES):  # 如果角化边之后，只含有边的关系,则使用边的关系去判断三角形形状
        #     pass
        # 组合边的方程组
        sides_eqs_combin = co.combin_iters(*sides_eqs)
        self.steps.append(['三角形形状为:', ''])
        try:
            shapes = []
            base_eqs_list = map(BaseEqs, sides_eqs_combin)
            for b_eqs in base_eqs_list:
                tmp_symbs = XingZhuang006.base_eqs_symbs(b_eqs)
                if not FiniteSet(*tmp_symbs).is_subset(SIDES):
                    raise Exception('try error')
                if len(tmp_symbs) == len(b_eqs.sympify()):  # 未知变量的个数与方程个数相等
                    sides_values = co.solve_poly_eqs(b_eqs)
                else:
                    a_symb, b_symb, c_symb = tmp_symbs
                    eqs = map(lambda _: _[0] - _[1], b_eqs.sympify())
                    sides_values = solve(eqs, a_symb, b_symb)
                    legal_vals = filter(
                        lambda _: XingZhuang006.legal_sided(_[0]) and XingZhuang006.legal_sided(_[1]),
                        sides_values)  # 合法的边的值
                    legal_vals = filter(lambda _: XingZhuang006.legal_sides((_[0], _[1], c_symb)), legal_vals)
                    if legal_vals:
                        for lv in legal_vals:
                            tmp_shape = triangle_shape002(zip((a_symb, b_symb), lv))
                            shapes.append(tmp_shape)
            for shape in shapes:
                self.output.append(str(shape))
            return self
        except Exception:
            raise Exception('try error')
        # -----------------所有关系转化为角的关系去处理--------------------------
        raise Exception('try error')


# singleTest('在△ABC中,若sinAsinB<cosAcosB,则△ABC的形状是()')
# paramer1: ineq
class XingZhuang007(BaseFunction):
    def solver(self, *args):
        ineq = args[0]
        l_expr, ineq_op, r_expr = ineq.sympify()
        src_f = l_expr - r_expr
        f = SanJiaoHuaJian(verbose=True).solver(l_expr - r_expr).output[0].sympify()
        angle1, angle2 = map(sympify, list(sorted(map(str, f.free_symbols))))
        other_angle = list(FiniteSet(*symbols('A, B, C')) - FiniteSet(angle1, angle2))[0]
        self.steps.append(['若' + ineq.printing(), ''])
        self.steps.append(['则' + new_latex(src_f) + ineq_op, '0,'])
        self.steps.append(['即' + new_latex(f) + ineq_op, '0,'])
        self.steps.append([r'\because 在 \vartriangle ABC 中 A+B+C=' + new_latex(pi), ''])
        self.steps.append([r'\therefore ' + new_latex(angle1 + angle2), '=' + new_latex(pi - other_angle)])
        sub_f = f.subs(angle1 + angle2, pi - other_angle)
        self.steps.append(['即 ' + new_latex(sub_f) + ineq_op + '0', ''])
        intl = solveset(str(sub_f) + ineq_op + '0', domain=S.Reals)
        if intl.intersect(Interval.open(0, pi / 2)) == Interval.open(0, pi / 2):
            shape = '锐角'
        else:
            shape = '钝角'
        self.steps.append(['即\\vartriangle ABC为 ' + shape, '三角形'])
        self.output.append(BaseString(shape))
        return self


# 利用推导判断三角形的形状
# Input paramer1:baseEq或者baseEqs
class XiangZhuang01(BaseFunction):
    @staticmethod
    def _right_triangle(eq):
        """
        直角三角形
        :param eq: 等式关系
        :return:
        """
        l, r = eq
        f = sympify(str(sympify(l) - sympify(r)))
        combins = ('a**2 - b**2 - c**2', 'b**2 - a**2 - c**2', 'c**2 - a**2 - b**2')
        for combin in combins:
            combin = sympify(combin)
            ration_v = simplify(f / combin)
            if ration_v.is_real:
                return True
        _A, _B, _C = symbols('A, B, C')
        if len(f.free_symbols) == 1 and su.i_is_subset(f.free_symbols, {_A, _B, _C}):
            f = expand_trig(f)
            angle = list(f.free_symbols)[0]
            sin_values = solve(f, sin(angle))
            if sin_values and sin_values[0] == 1:
                return True
            cos_values = solve(f, cos(angle))
            if cos_values and cos_values[0] == 0:
                return True
        return False

    @staticmethod
    def _equilateral_triangle(eq):
        """
        等边三角形
        :param eq:
        :return:
        """
        l, r = eq
        _ = sympify(l) - sympify(r)
        return False

    @staticmethod
    def _isosceles_triangle(eq):
        """
        等腰三角形
        :param eq:
        :return:
        """
        l, r = eq
        f = sympify(str(sympify(l) - sympify(r)))
        combins = ('a - b', 'a - c', 'b - c')
        for combin in combins:
            combin = sympify(combin)
            ration_v = simplify(f / combin)
            if ration_v.is_real:
                return True
        return False

    def solver(self, *args):
        self.label.add(u'三角形形状判定')
        cond_eqs = sanjiaoxing_utils.known_triangle_eqs(self.known).sympify()
        steps = []
        labels = []
        shapes = []  # 形状
        from mathsolver.functions.sanjiao.sanjiaoxingqiuzhi import condition_deep_deduce_v2, extract_kd_steps_and_labels
        _a, _b, _c = symbols('a, b, c')

        def filter_side_eq(eqs, kds):
            for (l, r), _kd in zip(eqs, kds):
                _f = sympify(l) - sympify(r)
                if su.i_is_subset(_f.free_symbols, {_a, _b, _c}):
                    return [l, r], _kd
            return None

        if len(cond_eqs) == 1:
            cond_eq_converts, cond_kds = condition_deep_deduce_v2(BaseEq(cond_eqs[0]))
            for eq, kd in zip(cond_eq_converts, cond_kds):
                if XiangZhuang01._right_triangle(eq):  # 直角三角形
                    shapes.append(u'直角')
                    kd_steps, kd_labels = extract_kd_steps_and_labels(kd)
                    steps.extend(kd_steps)
                    steps.append([u'得出三角形为', u'直角三角形'])
                    labels.extend(kd_labels)
                elif XiangZhuang01._equilateral_triangle(eq):  # 等腰三角形
                    shapes.append(u'等腰')
                    kd_steps, kd_labels = extract_kd_steps_and_labels(kd)
                    steps.extend(kd_steps)
                    steps.append([u'得出三角形为', u'等腰三角形'])
                    labels.extend(kd_labels)
        elif len(cond_eqs) == 2:
            cond1_eq_converts, cond1_kds = condition_deep_deduce_v2(baseEq(cond_eqs[0]))
            cond2_eq_converts, cond2_kds = condition_deep_deduce_v2(baseEq(cond_eqs[1]))
            cond1_eq, cond1_kd = filter_side_eq(cond1_eq_converts, cond1_kds)
            cond2_eq, cond2_kd = filter_side_eq(cond2_eq_converts, cond2_kds)
            sides = sorted(co.list_symbols([cond1_eq, cond2_eq]), key=str)
            side1, side2 = sides[:2]
            # side3 = list({_a, _b, _c} - {side1, side2})[0]
            side_values_list = co.isolve_eqs(baseEqs([cond1_eq, cond2_eq]), [side1, side2])
            positive_subs = (('a', Symbol('a', positive=True)),
                             ('b', Symbol('b', positive=True)), ('c', Symbol('c', positive=True))
                             )
            solved = False  # 是否已经解出来了
            suffix_steps = []  # 后置步骤
            for side_values in side_values_list:
                side_values = filter(
                    lambda _: (not str(_[1]).find('Root') >= 0) and co.is_sympify_expr(_[1]) and not isinstance(
                        _[1], (Interval, Union)), side_values)
                side_values_dict = dict(side_values)
                side1_value = side_values_dict.get(side1, None)
                sv1 = side1_value.subs(positive_subs) if side1_value else None
                side2_value = side_values_dict.get(side2, None)
                sv2 = side2_value.subs(positive_subs) if side2_value else None
                if sv1 and sv2 and sv1 > 0 and sv2 > 0:
                    v1 = simplify(sv1 ** 2 + sv2 ** 2)
                    v1_2 = simplify(sv1 ** 2 - sv2 ** 2)
                    eq1 = [side1 ** 2 + side2 ** 2, v1]
                    eq1_2 = [side1 ** 2 - side2 ** 2, v1_2]
                    v2 = simplify(sv1 - sv2)
                    eq2 = [side1 - side2, v2]
                    eq3 = [side1, sv1]
                    eq4 = [side2, sv2]
                    if XiangZhuang01._right_triangle(eq1):  # 直角三角形
                        shapes.append(u'直角')
                        solved = True
                        suffix_steps.append([u'由' + baseEq(eq1).printing(), u'得出三角形为直角三角形'])
                    if XiangZhuang01._right_triangle(eq1_2):
                        shapes.append(u'直角')
                        solved = True
                        suffix_steps.append([u'由' + baseEq(eq1_2).printing(), u'得出三角形为直角三角形'])
                    if XiangZhuang01._isosceles_triangle(eq2):  # 等腰三角形
                        solved = True
                        shapes.append(u'等腰')
                        suffix_steps.append([u'由' + baseEq(eq2).printing(), u'得出三角形为直角三角形'])
                    if XiangZhuang01._isosceles_triangle(eq3):
                        solved = True
                        shapes.append(u'等腰')
                        suffix_steps.append([u'由' + baseEq(eq3).printing(), u'得出三角形为直角三角形'])
                    if XiangZhuang01._isosceles_triangle(eq4):
                        solved = True
                        shapes.append(u'等腰')
                        suffix_steps.append([u'由' + baseEq(eq4).printing(), u'得出三角形为直角三角形'])
            if solved:
                kd1_steps, kd1_labels = extract_kd_steps_and_labels(cond1_kd)
                steps.extend(kd1_steps)
                steps.append([u'', u''])
                kd2_steps, kd2_labels = extract_kd_steps_and_labels(cond2_kd)
                steps.extend(kd2_steps)
                labels.extend(kd2_labels)
                steps.extend(suffix_steps)
        else:
            raise Exception('Illegal parameter')
        self.steps.extend(steps)
        for label in labels:
            self.label.add(label)
        shape_name_map = {u'直角': 0, u'等边': 1, u'等腰': 2}
        shapes = sorted(set(shapes), key=lambda _: shape_name_map[_])
        t_shape = u''.join(shapes)
        self.steps.append([u'所以三角形为', t_shape + u'三角形'])
        return self


class SanJiaoXingXingZhuang(BaseFunction):
    CLS = [XiangZhuang01, XingZhuang001, XingZhuang002, XingZhuang003, XingZhuang004, XingZhuang005, XingZhuang006, XingZhuang007]

    def solver(self, *args):
        cond = self.known
        if 'triangle' in cond:
            del cond['triangle']
        cond_type = ''
        if cond.get(sympify('type')):
            cond_type = str(list(cond[sympify('type')])[0])
            del cond[sympify('type')]
        new_args = []
        if cond_type == 'eqs':
            new_eqs = []
            for t1, t2 in list(cond.items()):
                if t1 not in ['equations', 'inequations', 'optimize', 'func_equations', 'func_assign',
                              'func_piecewise', 'processed']:
                    new_eqs.append([t1, list(t2)[0]])
            new_args.append(BaseEqs(new_eqs))
        elif cond_type == 'sineq':
            eqs = sanjiaoxing_utils.known_triangle_eqs(cond).sympify()
            eq = eqs[0]
            new_args.append(BaseEq(eq))
        elif cond_type == 'ineq':
            ineq = BaseIneq(co.to_ineq_expr_list(str(list(cond[sympify('triangle_ineq')])[0])))
            new_args.append(ineq)
        # new_args.append(args[0])
        new_args.extend([arg for arg in args])
        r = None
        for cl in SanJiaoXingXingZhuang.CLS:
            try:
                r = cl(verbose=True, known=self.known).solver(*new_args)
                r.label.add('判断三角形形状')
                break
            except Exception:
                pass
        if not r:
            raise Exception('Can not solve the question')
        return r


if __name__ == '__main__':
    pass
    # print '------------------xiangzhuang test----------'
    # SanJiaoXingXingZhuang(verbose=True).solver(BaseEqs([['a', '3'], ['b', '4'], ['c', '5']]))
    # print SanJiaoXingXingZhuang(verbose=True).solver(BaseEqs([['a', '3'], ['b', '4'], ['c', '5']]))
    # print SanJiaoXingXingZhuang(verbose=True).solver(BaseEq(['b*cos(C) + c*cos(B)', 'a*sin(A)'])).output[0].sympify()
